package top.codedance.iotp.iot.sdk;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import top.codedance.iotp.iot.sdk.entity.DistributedRegistyUris;
import top.codedance.iotp.iot.sdk.po.async.AsyncBaseParam;
import top.codedance.iotp.iot.sdk.po.async.CommonParam;
import top.codedance.iotp.iot.sdk.po.async.MessageParam;
import top.codedance.iotp.loadblance.common.AlgorithmType;
import org.java_websocket.handshake.ServerHandshake;

import javax.xml.bind.DatatypeConverter;
import java.nio.charset.StandardCharsets;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;

public class DistributedWsSimpleClient {
    private static DistributedWsSimpleClient self = null;

    private CommonParam commonParam;
    private DistributedWsService distributedWsService;
    private Set<SimpleClientMessageAdapter> listeners = new HashSet<>();
    private boolean isOpend = false;
    private final CountDownLatch latch = new CountDownLatch(1);

    private DistributedWsSimpleClient(DistributedRegistyUris registyHosts, String route, String username, String password){
        this(registyHosts, route, username, password, "-1", false);
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, String route, String username, String password){
        if(self == null)
            synchronized (DistributedWsSimpleClient.class){
                if(self == null){
                    self = new DistributedWsSimpleClient(registyHosts, route, username, password);
                }
            }
        return self;
    }

    private DistributedWsSimpleClient(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId, boolean isReturnBinary){
        this(registyHosts, route, username, password, deviceId, 0, isReturnBinary);
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId, boolean isReturnBinary){
        if(self == null)
            synchronized (DistributedWsSimpleClient.class){
                if(self == null){
                    self = new DistributedWsSimpleClient(registyHosts, route, username, password, deviceId, isReturnBinary);
                }
            }
        return self;
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId){
        return getInstance(registyHosts, route, username, password, deviceId, false);
    }

    private DistributedWsSimpleClient(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId, Integer instance, boolean isReturnBinary){
        this(registyHosts, AlgorithmType.AUTO, route, username, password, deviceId, instance, isReturnBinary);
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId, Integer instance, boolean isReturnBinary){
        if(self == null)
            synchronized (DistributedWsSimpleClient.class){
                if(self == null){
                    self = new DistributedWsSimpleClient(registyHosts, route, username, password, deviceId, instance, isReturnBinary);
                }
            }
        return self;
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, String route, String username, String password, String deviceId, Integer instance){
        return getInstance(registyHosts, route, username, password, deviceId, instance, false);
    }

    private DistributedWsSimpleClient(DistributedRegistyUris registyHosts, AlgorithmType at, String route, String username, String password, String deviceId, Integer instance, boolean isReturnBinary){
        AsyncBaseParam asyncBaseParam = new AsyncBaseParam();
        asyncBaseParam.setUsername(username);
        asyncBaseParam.setPassword(password);
        commonParam = new CommonParam(asyncBaseParam);
        commonParam.setInstance(instance);
        new Thread(() -> {
            distributedWsService = new DistributedWsService(registyHosts, route, at, deviceId);
            distributedWsService.run(commonParam, new WsCallbackAdapter() {
                @Override
                public void onOpen(WsContextAdapter context, CommonParam cp, ServerHandshake shake) {
                    isOpend = true;
                    try {
                        Thread.sleep(500);
                    }catch (Exception ex){}
                    context.send(commonParam.toString());
                    for (SimpleClientMessageAdapter scml : listeners){
                        scml.open();
                    }
                    latch.countDown();
                    System.out.println("opened");
                }

                @Override
                public void onMessage(WsContextAdapter context, CommonParam cp, String body) {
                    for (SimpleClientMessageAdapter scml : listeners){
                        if(isReturnBinary) {
                            scml.call(getSourceString(body));
                        }else{
                            scml.call(body);
                        }
                    }
                }

                @Override
                public void onClose(WsContextAdapter context, CommonParam cp, int var1, String var2, boolean var3) {
                    isOpend = false;
                    for (SimpleClientMessageAdapter scml : listeners){
                        scml.close();
                    }
                    System.out.println("closed");
                }

                @Override
                public void onError(WsContextAdapter context, CommonParam cp, Exception e) {
                    for (SimpleClientMessageAdapter scml : listeners){
                        scml.error(e.getMessage());
                    }
                    System.out.println("onError");
                }
            });
            distributedWsService.waiting();
        }).start();


        try{
            Thread.sleep(2000);
        }catch (Exception e){}
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, AlgorithmType at, String route, String username, String password, String deviceId, Integer instance, boolean isReturnBinary){
        if(self == null)
            synchronized (DistributedWsSimpleClient.class){
                if(self == null){
                    self = new DistributedWsSimpleClient(registyHosts, at, route, username, password, deviceId, instance, isReturnBinary);
                }
            }
        return self;
    }

    public static DistributedWsSimpleClient getInstance(DistributedRegistyUris registyHosts, AlgorithmType at, String route, String username, String password, String deviceId, Integer instance){
        return getInstance(registyHosts, at, route, username, password, deviceId, instance, false);
    }

    /**
     * 注册监听器
     * @param listener
     */
    public void addListener(SimpleClientMessageAdapter listener){
        listeners.add(listener);
    }

    /**
     * 发消息
     * @param body
     */
    public void send(JSON body){
        try {
            latch.await(15, TimeUnit.SECONDS);
            if (isOpend) {
                MessageParam messageParam = new MessageParam(commonParam);
                messageParam.setBody(body);
                distributedWsService.send(messageParam.toString());
            }
        }catch (Exception e){
            System.out.println(e.getMessage());
        }
    }

    /**
     * 等待若干秒
     * @param second
     */
    public void waiting(long second){
        try{
            Thread.sleep(1000 * second);
        }catch (Exception e){}
    }

    public boolean isRunning(){
        return isOpend;
    }

    public void close(){
        distributedWsService.stop();
    }

    private String getSourceString(String data){
        try{
            JSON.parseObject(data);
            return data;
        } catch (Exception e){
            return new String(DatatypeConverter.parseHexBinary(data), StandardCharsets.UTF_8);
        }
    }
}
